package cn.edu.cug.cs.gtl.docdb.core;

import cn.edu.cug.cs.gtl.common.Identifier;
import cn.edu.cug.cs.gtl.common.Pair;
import cn.edu.cug.cs.gtl.docdb.core.impl.DocQuerierImpl;
import cn.edu.cug.cs.gtl.docdb.core.impl.DocUpdaterImpl;
import cn.edu.cug.cs.gtl.io.File;
import cn.edu.cug.cs.gtl.io.storage.StorageManager;
import cn.edu.cug.cs.gtl.protos.Attachments;
import cn.edu.cug.cs.gtl.protos.Document;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.hadoop.fs.FileSystem;

import java.io.IOException;
import java.sql.Connection;
import java.sql.Date;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 文档库
 * 1、文档目录的导入
 * 2、查询
 * 3、更新
 *
 */
public class DocStore implements AutoCloseable{
    //存储文档元数据和分类目录的信息
    Connection connection;
    //存放文档原始数据块的存储设备
    StorageManager dataStorageManager;

    public Connection getConnection() {
        return connection;
    }

    public StorageManager getStorageManager() {
        return dataStorageManager;
    }

    /**
     *
     * @return
     */
    public DocQuerier getQuerier(){
        return new DocQuerierImpl(this);
    }

    /**
     *
     * @return
     */
    public DocUpdater getUpdater(){
        return new DocUpdaterImpl(this);
    }
    /**
     *
     * @param connection
     * @param dataStorageManager
     * @param overwrite
     * @throws Exception
     */
    public DocStore(Connection connection, StorageManager dataStorageManager, boolean overwrite) throws Exception {
        this.dataStorageManager=dataStorageManager;
        this.connection=connection;
        if(overwrite){
            DocCatalog.dropCatalogTable(connection);
            DocCatalog.createCatalogTable(connection);
            DocMeta.dropMetaTable(connection);
            DocMeta.createMetaTable(connection);
        }
    }

    @Override
    public void close() throws Exception{
        connection.close();
        dataStorageManager.close();
    }


    /**
     * 从本地文件目录中提取文件存入文档库中
     * @param parentCatalog 目录库中的父目录，如果是null，则没有父目录
     * @param rootDir  不支持根目录，例如"/"是不支持的
     * @return
     */
    public boolean importFromLocalFileSystem(DocCatalog parentCatalog, String rootDir) throws Exception {
        //分目录和文件
        File rootFile = new File(rootDir);
        List<File> files = new ArrayList<>();
        List<File> dirs = new ArrayList<>();
        {
            java.io.File [] files1 = rootFile.listFiles(f->f.isFile());
            java.io.File [] dirs1 = rootFile.listFiles(f->f.isDirectory());
            for(java.io.File f: files1){
                files.add(new File(f.getAbsolutePath()));
            }
            for(java.io.File f: dirs1){
                dirs.add(new File(f.getAbsolutePath()));
            }
        }
        //计算rootName
        String [] sa = rootFile.split();
        String rootName = sa[sa.length-1];
        DocCatalog rootCatalog = new DocCatalog();
        rootCatalog.setName(rootName);
        rootCatalog.setIdentifier(DocCatalog.next(connection));
        //1.处理rootDir目录
        if(parentCatalog!=null){
            rootCatalog.setParentIdentifier(parentCatalog.getIdentifier());
        }
        //2.处理所有的子目录
        for(File f: dirs){
            String dirName = f.getFileName();
            DocCatalog dirCatalog = new DocCatalog();
            dirCatalog.setName(dirName);
            dirCatalog.setIdentifier(DocCatalog.next(connection));
            Identifier identifier = (Identifier) rootCatalog.getIdentifier().clone();
            dirCatalog.setParentIdentifier(identifier);
        }
        //3.处理所有的文件
        for(File f: files){
            DocMeta docMeta = new DocMeta();
            docMeta.setParentIdentifier(Identifier.create(-1));
            docMeta.setCatalogIdentifier(Identifier.create(-1));
            docMeta.setTitle(f.getFileNameWithoutSuffix());
            docMeta.setType(f.getSuffixName());
            java.util.Date date = new java.util.Date();
            docMeta.setAppendDate(date);
            docMeta.setCreateDate(date);
            docMeta.setUpdateDate(date);
            docMeta.setComment(f.getFileName());
            List<String> strings = new ArrayList<>();
            strings.add("a");
            strings.add("b");
            strings.add("c");
            docMeta.setKeywords(strings);
            //.......
            docMeta.setCatalogIdentifier((Identifier) rootCatalog.getIdentifier().clone());

            DocRaw docRaw = new DocRaw();
            docRaw.setInternalIdentifier(Identifier.create(StorageManager.NEW_PAGE));
            docRaw.setRawData(File.readRawFile(f).array());
            //写入原始数据
            Identifier identifier = DocRaw.write(dataStorageManager,docRaw);
            //插入元数据
            docMeta.setIdentifier((Identifier)identifier.clone());
            DocMeta.insert(connection,docMeta);
        }
        files.clear();
        //4. 写入root目录数据
        DocCatalog.insert(connection,rootCatalog);
        //5.递归处理rootDir目录下的子目录
        for(File f: dirs){
            importFromLocalFileSystem(rootCatalog,f.getAbsolutePath());
        }
        return true;
    }


    /**
     *
     * 从FTP文件目录中提取文件存入文档库中
     * @param client
     * @param parentCatalog 目录库中的父目录，如果是-1L，则没有父目录
     * @param rootDir
     * @return
     */
    public boolean importFromFTPFileSystem(FTPClient client, Identifier parentCatalog,String rootDir){
        return false;
    }

    /**
     *
     * 从HDFS文件目录中提取文件存入文档库中
     * @param fileSystem
     * @param parentCatalog 目录库中的父目录，如果是-1L，则没有父目录
     * @param rootDir
     * @return
     */
    public boolean importFromHDFSFileSystem(FileSystem fileSystem,Identifier parentCatalog, String rootDir){
        return false;
    }

    /**
     * 将文档解析成DocMeta和DocData
     * @param document
     * @return
     */
    public static Pair<DocMeta, DocRaw> parse(Document document) throws ParseException {
        Pair<DocMeta, DocRaw> docMetaDocRawPair = new Pair<>();
        DocMeta docMeta = new DocMeta();
        DocRaw docRaw = new DocRaw();
        if (document.getInternalId() != 0) {
            docMeta.setIdentifier(Identifier.create(document.getInternalId()));
            docRaw.setInternalIdentifier(Identifier.create(document.getInternalId()));
        } else {
            docMeta.setIdentifier(Identifier.randomIdentifier());
            docRaw.setInternalIdentifier(Identifier.randomIdentifier());
        }
        if (document.getCatalog() != 0) {
            docMeta.catalogIdentifier = Identifier.create(document.getCatalog());
        } else {
            docMeta.catalogIdentifier = Identifier.create(-1);
        }
        docMeta.parentIdentifier = Identifier.create(-1);
        if (document.getBoundingCount() != 0) {
            docMeta.bounding = document.getBounding(0);
        }
        docMeta.title = document.getTitle();
        docMeta.memo = document.getMemo();
        docMeta.uuid = document.getUuid();
        docMeta.comment = document.getComment();
        docMeta.schema = document.getSchema();
        docMeta.type = document.getType();
        docMeta.version = document.getVersion();
        docMeta.url = document.getUrl();
        docMeta.content = document.getContentList().toString();
        docMeta.abstractText = document.getAbstract();
        docMeta.affiliations = document.getAffiliationList();
        docMeta.keywords = document.getKeywordList();
        docMeta.tags = document.getTagList();
        docMeta.authors = document.getAuthorList();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        if (document.getAppendDate() != "") {
            docMeta.appendDate = simpleDateFormat.parse(document.getAppendDate());
        } else {
            docMeta.appendDate = new java.util.Date();
        }
        if (document.getCreateDate() != "") {
            docMeta.createDate = simpleDateFormat.parse(document.getCreateDate());
        } else {
            docMeta.createDate = new java.util.Date();
        }
        if (document.getUpdateDate() != "") {
            docMeta.updateDate = simpleDateFormat.parse(document.getUpdateDate());
        } else {
            docMeta.updateDate = new java.util.Date();
        }
        docRaw.setRawData(document.getRawData().toByteArray());
        docRaw.setAttachments(document.getAttachments().toByteArray());
        docMetaDocRawPair.setKey(docMeta);
        docMetaDocRawPair.setValue(docRaw);
        return docMetaDocRawPair;
    }


    /**
     *
     * @param docMeta
     * @param docRaw
     * @return
     */
    public static Document merge(DocMeta docMeta, DocRaw docRaw) throws InvalidProtocolBufferException {
        Document.Builder builder = Document.newBuilder();
        builder.setInternalId(docMeta.getInternalIdentifier().longValue());
        builder.setTitle(docMeta.getTitle());
        builder.setVersion(docMeta.getVersion());
        builder.setType(docMeta.getType());
        builder.addAllKeyword(docMeta.getKeywords());
        builder.setUrl(docMeta.getUrl());
        builder.addAllAuthor(docMeta.getAuthors());
        builder.addAllAffiliation(docMeta.getAffiliations());
        builder.setAbstract(docMeta.getAbstractText());
        builder.addAllContent(Arrays.asList(docMeta.getContent().split(",")));
        builder.setSchema(docMeta.getSchema());
        builder.setBounding(0,docMeta.getBounding());
        builder.addAllTag(docMeta.getTags());
        builder.setComment(docMeta.getComment());
        builder.setCatalog(docMeta.getCatalogIdentifier().longValue());
        builder.setUuid(docMeta.getUuid());
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        builder.setAppendDate(sdf.format(docMeta.getAppendDate()));
        builder.setCreateDate(sdf.format(docMeta.getCreateDate()));
        builder.setUpdateDate(sdf.format(docMeta.getUpdateDate()));
        builder.setMemo(docMeta.getMemo());
        builder.setRawData(ByteString.copyFrom(docRaw.getRawData()));
        builder.setAttachments(Attachments.parseFrom(docRaw.getAttachments()));
        ////////........
        return builder.build();
    }

}
